/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2023 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "dma.h"
#include "usart.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "cJSON.h"
#include "nb.h"
#include "config.h"
#include <stdlib.h>
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
typedef struct
{
    uint8_t  valid;
    uint16_t rh;
    uint16_t temp;
    uint16_t db;
    uint16_t pm25;
    uint16_t pm10;
} monitor_data_t;
monitor_data_t monitor_data;
/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define DAY_TICK 86400000 //(24*60*60*1000)
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */
#define TCP_BUFF_SIZE_MAX 256
uint8_t  tcp_rx_buff[TCP_BUFF_SIZE_MAX];
uint16_t tcp_rx_size;
uint8_t  tcp_tx_buff[TCP_BUFF_SIZE_MAX];
uint16_t tcp_tx_size;
uint16_t period_elapsedtime = 0;
uint16_t nb_tcp_elapsedtime = 0;

volatile uint8_t is_time_up = 0;

uint32_t last_tick, now_tick;
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
void soft_reset(void)
{
    __set_PRIMASK(1);
    NVIC_SystemReset();
}

void HAL_SYSTICK_Callback(void)
{
    static volatile uint16_t nbTick = 0;
    static volatile uint16_t watchdogFeedTime = 0;
    static volatile uint16_t statusTick = 0;
    if (watchdogFeedTime < 1000) {
        watchdogFeedTime++;

        nbTick++;
        switch (nbled) {
            case NB_IDLE:
                led_g_off();
                break;
            case NB_CHECK_AT:
                if (nbTick < 500) {
                    led_g_off();
                } else if (nbTick < 1000) {
                    led_g_on();
                } else {
                    nbTick = 0;
                }
                break;
            case NB_ATTACH_NET:
                if (nbTick < 200) {
                    led_g_off();
                } else if (nbTick < 400) {
                    led_g_on();
                } else {
                    nbTick = 0;
                }
                break;
            case NB_TRY_CONNECT:
                if (nbTick < 500) {
                    led_g_off();
                } else if (nbTick < 600) {
                    led_g_on();
                } else {
                    nbTick = 0;
                }
                break;
            case NB_CONNECTED:
                if (nbTick < 900) {
                    led_g_on();
                } else if (nbTick < 1000) {
                    led_g_off();
                } else {
                    nbTick = 0;
                }
                break;
            case NB_DISCONNECTED:
                if (nbTick < 900) {
                    led_g_off();
                } else if (nbTick < 1000) {
                    led_g_on();
                } else {
                    nbTick = 0;
                }
                break;
            default:
                nbled = NB_IDLE;
                break;
        }

        if (nbTick < 5000) {
            led_r_off();
        } else if (nbTick < 5100) {
            led_r_on();
        } else {
            nbTick = 0;
        }

        statusTick++;
        switch (config_status) {
            case IDLE:
                led_r_off();
                break;
            case CONFIG_INVALID:
                if (statusTick < 200) {
                    led_r_off();
                } else if (statusTick < 400) {
                    led_r_on();
                } else {
                    statusTick = 0;
                }
                break;
            case NORMAL:
                if (statusTick < 5000) {
                    led_r_off();
                } else if (statusTick < 5100) {
                    led_r_on();
                } else {
                    statusTick = 0;
                }
                break;
            default:
                config_status = IDLE;
                break;
        }

    } else {
        watchdogFeedTime = 0;
        wdg_feed();

        if (nb_tcp_elapsedtime) {
            nb_tcp_elapsedtime--;
        }

        if (period_elapsedtime) {
            period_elapsedtime--;
            if (0 == period_elapsedtime) {
                is_time_up = 1;
            }
        }
    }
}

uint8_t read_monitor(void)
{
    //4800 8 N 1?? ???1
    //??????
    //Tx:01 03 01 F4 00 05 C5 C7
    //Rx:01 03 0A 02 35 00 F2 01 EC 00 17 00 1C 1C 29
    //02 35 =565/10 RH
    //00 F2 =242/10 C
    //01 EC = 492/10 dB
    //00 17 = 23 ug/m3
    //00 1C = 28 ug/m3

    uint8_t cmd[8] = { 0x01, 0x03, 0x01, 0xF4, 0x00, 0x05, 0xc5, 0xc7 };
    uint8_t i = 0, dt = 0;
    char    buff[10];

    usart_rx_buff_clear(&RS485_UART_RX);

    msg_printf("rs485 tx:");
    msg_printf_hex(cmd, sizeof(cmd));
    HAL_Delay(100);
    usart_send_data(&RS485_UART, cmd, sizeof(cmd));

    while (!RS485_UART_RX.flag && dt++ < 20) {
        HAL_Delay(100);
        msg_printf(".");
    }

    if (RS485_UART_RX.flag && RS485_UART_RX.len == 15) {
        RS485_UART_RX.flag = 0;
        msg_printf("done\r\n");
        msg_printf("rx len=%d: ", RS485_UART_RX.len);
        msg_printf_hex(RS485_UART_RX.buff, RS485_UART_RX.len);

        memset(&monitor_data, 0, sizeof(monitor_data));
        monitor_data.valid = 1;
        monitor_data.rh = (RS485_UART_RX.buff[3] << 8 | RS485_UART_RX.buff[4]);
        monitor_data.temp = (RS485_UART_RX.buff[5] << 8 | RS485_UART_RX.buff[6]);
        monitor_data.db = (RS485_UART_RX.buff[7] << 8 | RS485_UART_RX.buff[8]);
        monitor_data.pm25 = (RS485_UART_RX.buff[9] << 8 | RS485_UART_RX.buff[10]);
        monitor_data.pm10 = (RS485_UART_RX.buff[11] << 8 | RS485_UART_RX.buff[12]);

        msg_printf("rh=%.2f,rh=%.2f,db=%.2f,pm2.5=%.2f,pm10=%.2f\r\n", monitor_data.rh / 10.0f, monitor_data.temp / 10.0f, monitor_data.db / 10.0f, monitor_data.pm25 / 1.0f, monitor_data.pm10 / 1.0f);
        return 1;
    }

    return 0;
}

uint16_t build_monitor_data(uint8_t* buff, uint16_t max_size)
{
    uint16_t size;
    cJSON*   obj;

    obj = cJSON_CreateObject();
    cJSON_AddItemToObject(obj, "cmd", cJSON_CreateString("data"));
    cJSON_AddItemToObject(obj, "sn", cJSON_CreateString((char*)imei));
    if (monitor_data.valid) {
        cJSON_AddItemToObject(obj, "valid", cJSON_CreateBool(1));
        cJSON_AddItemToObject(obj, "rh", cJSON_CreateNumber(monitor_data.rh));
        cJSON_AddItemToObject(obj, "temp", cJSON_CreateNumber(monitor_data.temp));
        cJSON_AddItemToObject(obj, "dB", cJSON_CreateNumber(monitor_data.db));
        cJSON_AddItemToObject(obj, "pm2.5", cJSON_CreateNumber(monitor_data.pm25));
        cJSON_AddItemToObject(obj, "pm10", cJSON_CreateNumber(monitor_data.pm10));
    } else {
        cJSON_AddItemToObject(obj, "valid", cJSON_CreateBool(0));
    }
    char* print_out = cJSON_PrintUnformatted(obj);
    msg_printf("json size = %ld string:\r\n%s\r\n", strlen(print_out), print_out);

    size = strlen(print_out);
    if (max_size < size) {
        memcpy(buff, print_out, max_size);
        size = max_size;
    } else {
        memcpy(buff, print_out, size);
    }

    free(print_out);
    cJSON_Delete(obj);

    return size;
}

uint16_t build_response_data(uint8_t* buff, uint16_t max_size, char* cmd, int code)
{
    uint16_t size;
    cJSON*   obj;

    obj = cJSON_CreateObject();
    cJSON_AddItemToObject(obj, "cmd", cJSON_CreateString(cmd));
    cJSON_AddItemToObject(obj, "sn", cJSON_CreateString((char*)imei));
    cJSON_AddItemToObject(obj, "code", cJSON_CreateNumber(code)); //200 for ok

    char* print_out = cJSON_PrintUnformatted(obj);
    msg_printf("json size = %ld string:\r\n%s\r\n", strlen(print_out), print_out);

    size = strlen(print_out);
    if (max_size < size) {
        memcpy(buff, print_out, max_size);
        size = max_size;
    } else {
        memcpy(buff, print_out, size);
    }

    free(print_out);
    cJSON_Delete(obj);

    return size;
}

void reset_tcp_elapsedtime(void)
{
    nb_tcp_elapsedtime = 10;
}

void handle_tcp_rx(uint8_t* buff, uint16_t size)
{
    cJSON* root = NULL;

    msg_printf("tcp rx:%s\r\n", (char*)buff);

    root = cJSON_Parse((char*)buff);
    if (NULL == root) {
        msg_printf("cjson parse error:%s\r\n", cJSON_GetErrorPtr());
        cJSON_Delete(root);
        return;
    }

    char* jsonstr = cJSON_Print(root);
    msg_printf("tcp json rx:\r\n%s\r\n", jsonstr);
    free(jsonstr);

    cJSON* json_cmd = cJSON_GetObjectItem(root, "cmd");
    if (NULL == json_cmd || cJSON_String != json_cmd->type) {
        msg_printf("cjson parse cmd error\r\n");
        cJSON_Delete(root);
        return;
    }

    //find cmd
    //@ipset
    if (memcmp(json_cmd->valuestring, "ipset", 5) == 0) {
        cJSON* json_sn = cJSON_GetObjectItem(root, "sn");
        cJSON* json_ip = cJSON_GetObjectItem(root, "ip");
        cJSON* json_port = cJSON_GetObjectItem(root, "port");
        if (NULL == json_sn || NULL == json_ip || NULL == json_port) {
            msg_printf("ipset cjson parse error\r\n");
            cJSON_Delete(root);
            return;
        }

        if (cJSON_String != json_sn->type || cJSON_String != json_ip->type || cJSON_Number != json_port->type) {
            msg_printf("ipset cjson type dismatch\r\n");
            cJSON_Delete(root);
            return;
        }

        if ((memcmp(json_sn->valuestring, "all", 3) == 0) || (memcmp(json_sn->valuestring, imei, sizeof(imei)) == 0)) {
            //for ip
            int ip[4];
            sscanf(json_ip->valuestring, "%d.%d.%d.%d", &ip[0], &ip[1], &ip[2], &ip[3]);
            if (ip[0] > 255 || ip[0] < 0 || ip[1] > 255 || ip[1] < 0 || ip[2] > 255 || ip[2] < 0 || ip[3] > 255 || ip[3] < 0) {
                msg_printf("ip error\r\n");
                cJSON_Delete(root);
                return;
            }
            eeprom_config.tcp.ip1 = ip[0];
            eeprom_config.tcp.ip2 = ip[1];
            eeprom_config.tcp.ip3 = ip[2];
            eeprom_config.tcp.ip4 = ip[3];
            msg_printf("ip ok\r\n");
            //for port
            eeprom_config.tcp.port = (uint16_t)json_port->valueint;
            msg_printf("port ok\r\n");
            save_config();
            //response
            tcp_tx_size = build_response_data(tcp_tx_buff, TCP_BUFF_SIZE_MAX, "ipset", 200);
            nb_tcp_send(tcp_tx_buff, tcp_tx_size);
            //done
            cJSON_Delete(root);
            msg_printf("reboot\r\n");
            soft_reset();
            return;
        } else {
            msg_printf("imei dismatch\r\n");
            cJSON_Delete(root);
            return;
        }
    }
    //
    //@period
    if (memcmp(json_cmd->valuestring, "periodset", 9) == 0) {
        cJSON* json_sn = cJSON_GetObjectItem(root, "sn");
        cJSON* json_period = cJSON_GetObjectItem(root, "period");
        if (NULL == json_sn || NULL == json_period) {
            msg_printf("periodset cjson parse error\r\n");
            cJSON_Delete(root);
            return;
        }

        if (cJSON_String != json_sn->type || cJSON_Number != json_period->type) {
            msg_printf("periodset cjson type dismatch\r\n");
            cJSON_Delete(root);
            return;
        }

        if ((memcmp(json_sn->valuestring, "all", 3) == 0) || (memcmp(json_sn->valuestring, imei, sizeof(imei)) == 0)) {
            //for port
            eeprom_config.period = (uint16_t)json_period->valueint;
            msg_printf("period ok\r\n");
            save_config();
            //response
            tcp_tx_size = build_response_data(tcp_tx_buff, TCP_BUFF_SIZE_MAX, "periodset", 200);
            nb_tcp_send(tcp_tx_buff, tcp_tx_size);
            //done
            cJSON_Delete(root);
            msg_printf("reboot\r\n");
            soft_reset();
            return;
        } else {
            msg_printf("imei dismatch\r\n");
            cJSON_Delete(root);
            return;
        }
    }

    msg_printf("cmd dismatch:%s\r\n", json_cmd->valuestring);
    cJSON_Delete(root);
}

void handle_debug_rx(uint8_t* buff, uint16_t size)
{
    cJSON* root = NULL;

    msg_printf("debug rx:%s\r\n", (char*)buff);

    root = cJSON_Parse((char*)buff);
    if (NULL == root) {
        msg_printf("cjson parse error:%s\r\n", cJSON_GetErrorPtr());
        cJSON_Delete(root);
        return;
    }

    char* jsonstr = cJSON_Print(root);
    msg_printf("debug json rx:\r\n%s\r\n", jsonstr);
    free(jsonstr);

    cJSON* json_cmd = cJSON_GetObjectItem(root, "cmd");
    if (NULL == json_cmd || cJSON_String != json_cmd->type) {
        msg_printf("cjson parse cmd error\r\n");
        cJSON_Delete(root);
        return;
    }

    //find cmd
    //@test monitor
    if (memcmp(json_cmd->valuestring, "test_monitor", 12) == 0) {
        cJSON* json_cnt = cJSON_GetObjectItem(root, "cnt");
        if (NULL == json_cnt) {
            msg_printf("test_monitor cjson parse error\r\n");
            cJSON_Delete(root);
            return;
        }

        if (cJSON_Number != json_cnt->type) {
            msg_printf("test_monitor cjson type dismatch\r\n");
            cJSON_Delete(root);
            return;
        }

        uint16_t cnt = json_cnt->valueint;

        pwr_on();
        for (int i = 0; i < cnt; i++) {
            read_monitor();
            HAL_Delay(1000);
        }
        pwr_off();
        cJSON_Delete(root);
        msg_printf("exit\r\n");
        return;
    }

    //@test nb tcp
    if (memcmp(json_cmd->valuestring, "test_nbtcp", 10) == 0) {
        cJSON* json_ip = cJSON_GetObjectItem(root, "ip");
        cJSON* json_port = cJSON_GetObjectItem(root, "port");
        cJSON* json_msg = cJSON_GetObjectItem(root, "msg");
        if (NULL == json_ip || NULL == json_port || NULL == json_msg) {
            msg_printf("test_nbtcp cjson parse error\r\n");
            cJSON_Delete(root);
            return;
        }

        if (cJSON_Number != json_port->type || cJSON_String != json_ip->type || cJSON_String != json_msg->type) {
            msg_printf("test_nbtcp cjson type dismatch\r\n");
            cJSON_Delete(root);
            return;
        }

        //for ip
        int ip[4], port;
        sscanf(json_ip->valuestring, "%d.%d.%d.%d", &ip[0], &ip[1], &ip[2], &ip[3]);
        if (ip[0] > 255 || ip[0] < 0 || ip[1] > 255 || ip[1] < 0 || ip[2] > 255 || ip[2] < 0 || ip[3] > 255 || ip[3] < 0) {
            msg_printf("ip error\r\n");
            cJSON_Delete(root);
            return;
        }
        //for port
        port = (uint16_t)json_port->valueint;
        tcp_tx_size = sprintf((char*)tcp_tx_buff, "%s", json_msg->valuestring);

        pwr_on();
        uint8_t res = nb_tcp_module_init(ip[0], ip[1], ip[2], ip[3], port);
        if (res) {
            nb_tcp_send(tcp_tx_buff, tcp_tx_size);
            nb_tcp_disconnect();
        }
        pwr_off();
        cJSON_Delete(root);
        return;
    }

    //@test config all
    if (memcmp(json_cmd->valuestring, "config_all", 10) == 0) {
        cJSON* json_ip = cJSON_GetObjectItem(root, "ip");
        cJSON* json_port = cJSON_GetObjectItem(root, "port");
        cJSON* json_period = cJSON_GetObjectItem(root, "period");
        if (NULL == json_ip || NULL == json_port || NULL == json_period) {
            msg_printf("test_nbtcp cjson parse error\r\n");
            cJSON_Delete(root);
            return;
        }

        if (cJSON_Number != json_period->type || cJSON_String != json_ip->type || cJSON_Number != json_port->type) {
            msg_printf("test_nbtcp cjson type dismatch\r\n");
            cJSON_Delete(root);
            return;
        }

        //for ip
        int ip[4], port, period;
        sscanf(json_ip->valuestring, "%d.%d.%d.%d", &ip[0], &ip[1], &ip[2], &ip[3]);
        if (ip[0] > 255 || ip[0] < 0 || ip[1] > 255 || ip[1] < 0 || ip[2] > 255 || ip[2] < 0 || ip[3] > 255 || ip[3] < 0) {
            msg_printf("ip error\r\n");
            cJSON_Delete(root);
            return;
        }
        eeprom_config.tcp.ip1 = ip[0];
        eeprom_config.tcp.ip2 = ip[1];
        eeprom_config.tcp.ip3 = ip[2];
        eeprom_config.tcp.ip4 = ip[3];
        //for port
        port = (uint16_t)json_port->valueint;
        eeprom_config.tcp.port = port;
        //for period
        period = (uint16_t)json_period->valueint;
        eeprom_config.period = period;
        config_set_valid();
        save_config();
        msg_printf("config parameters save ok, reboot\r\n");
        HAL_Delay(1000);
        soft_reset();
        cJSON_Delete(root);
        return;
    }

    msg_printf("cmd dismatch:%s\r\n", json_cmd->valuestring);
    cJSON_Delete(root);
}

void main_func()
{
    uint8_t         res;
    static uint32_t fail_cnt = 0;

    if (!read_config()) {
        return;
    }

    MX_USART1_UART_Init();
    MX_USART2_UART_Init();
    pwr_on();

    read_monitor();

    res = nb_tcp_module_init(eeprom_config.tcp.ip1, eeprom_config.tcp.ip2, eeprom_config.tcp.ip3, eeprom_config.tcp.ip4, eeprom_config.tcp.port);
    if (res) {
        tcp_tx_size = build_monitor_data(tcp_tx_buff, TCP_BUFF_SIZE_MAX);
        if (!nb_tcp_send(tcp_tx_buff, tcp_tx_size)) {
            fail_cnt++;
        } else {
            fail_cnt = 0;
        }
        reset_tcp_elapsedtime();
        while (nb_tcp_elapsedtime) {
            if (nb_tcp_get_module_buff_data()) {
                reset_tcp_elapsedtime();
                handle_tcp_rx(tcp_rx_buff, tcp_rx_size);
            }
            HAL_Delay(1000);
        }
        nb_tcp_disconnect();
    } else {
        fail_cnt++;
        msg_printf("nb tcp fail, cnt = %d\r\n", fail_cnt);
        if (3 == fail_cnt) {
            msg_printf("reboot\r\n");
            pwr_off();
            soft_reset();
        }
    }

    pwr_off();
    HAL_UART_MspDeInit(&huart1);
    HAL_UART_MspDeInit(&huart2);
}

void nb_usart_debug(void)
{
    while (1) {
        if (DEBUG_UART_RX.flag) {
            DEBUG_UART_RX.flag = 0;
            //handle_debug_rx(DEBUG_UART_RX.buff, DEBUG_UART_RX.len);
            msg_printf("nb tx:%s\r\n", DEBUG_UART_RX.buff);
            usart_send_data(&NB_UART, DEBUG_UART_RX.buff, DEBUG_UART_RX.len);
        }

        if (NB_UART_RX.flag) {
            NB_UART_RX.flag = 0;
            //usart_send_data(&DEBUG_UART, NB_UART_RX.buff, NB_UART_RX.len);
            msg_printf("nb rx:%s\r\n", NB_UART_RX.buff);
        }
    }
}
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
    /* USER CODE BEGIN 1 */

    /* USER CODE END 1 */

    /* MCU Configuration--------------------------------------------------------*/

    /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
    HAL_Init();

    /* USER CODE BEGIN Init */

    /* USER CODE END Init */

    /* Configure the system clock */
    SystemClock_Config();

    /* USER CODE BEGIN SysInit */

    /* USER CODE END SysInit */

    /* Initialize all configured peripherals */
    MX_GPIO_Init();
    MX_DMA_Init();
    MX_USART1_UART_Init();
    MX_USART2_UART_Init();
    MX_USART3_UART_Init();
    /* USER CODE BEGIN 2 */
    msg_printf("system startd\r\n");
    msg_printf("\r\n+++++++++++++++++ lu-shan environment monitor +++++++++++++++++\r\n\r\n");
    msg_printf("++++ version   : v1.0.0, build %s, %s\r\n", __DATE__, __TIME__);
    msg_printf("++++ author    : jianwang16@foxmail.com\r\n");
    msg_printf("++++ copyright : sichaun yunhe tech Co. @2023.7.18-, reserved\r\n\r\n");

    if (!read_config()) {
        msg_printf("+++++++++++++++++ go to degug config mode +++++++++++++++++\r\n");
        while (1) {
            //nb_usart_debug();
            if (DEBUG_UART_RX.flag) {
                DEBUG_UART_RX.flag = 0;
                handle_debug_rx(DEBUG_UART_RX.buff, DEBUG_UART_RX.len);
            }
        }
    }

    msg_printf("\r\nconfig server:%d.%d.%d.%d:%d\r\n",
               eeprom_config.tcp.ip1,
               eeprom_config.tcp.ip2,
               eeprom_config.tcp.ip3,
               eeprom_config.tcp.ip4,
               eeprom_config.tcp.port);
    msg_printf("\r\nconfig period:%d\r\n", eeprom_config.period);

    period_elapsedtime = eeprom_config.period * 60; //min
    main_func();
    /* USER CODE END 2 */

    /* Infinite loop */
    /* USER CODE BEGIN WHILE */
    while (1) {
        /* USER CODE END WHILE */

        /* USER CODE BEGIN 3 */
        now_tick = HAL_GetTick();
        if (now_tick - last_tick > 3000) {
            msg_printf("idle, tick:%u us, task:%u s\r\n", now_tick, period_elapsedtime);
            last_tick = now_tick;
        }

        if (period_elapsedtime == 0) {
            period_elapsedtime = eeprom_config.period * 60; //min
            main_func();
        }

        if (DEBUG_UART_RX.flag) {
            DEBUG_UART_RX.flag = 0;
            handle_debug_rx(DEBUG_UART_RX.buff, DEBUG_UART_RX.len);
        }

        //reboot control
        if (HAL_GetTick() > DAY_TICK) {
            soft_reset();
        }
    }
    /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
    RCC_OscInitTypeDef RCC_OscInitStruct = { 0 };
    RCC_ClkInitTypeDef RCC_ClkInitStruct = { 0 };

    /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
    RCC_OscInitStruct.HSIState = RCC_HSI_ON;
    RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
    RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI_DIV2;
    RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL8;
    if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
        Error_Handler();
    }
    /** Initializes the CPU, AHB and APB buses clocks
  */
    RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK
                                  | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
    RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
    RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
    RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

    if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK) {
        Error_Handler();
    }
}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
    /* USER CODE BEGIN Error_Handler_Debug */
    /* User can add his own implementation to report the HAL error return state */
    __disable_irq();
    msg_printf("error handler\r\n");
    while (1) {
    }
    /* USER CODE END Error_Handler_Debug */
}

#ifdef USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t* file, uint32_t line)
{
    /* USER CODE BEGIN 6 */
    /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
    /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
